/*
 * Copyright (c) 2006-2020, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-05-21     chenhy       the first version
 */
#include "rtc.h"
#include "board.h"
#include <stdio.h>
#include <time.h>

/* ck_apre=HSEFreq/32/(ASYNC prediv + 1) with LSIFreq=40 kHz RC */
#define RTC_ASYNCH_PREDIV          ((uint32_t)0x7C)
/* ck_spre=ck_apre/(SYNC prediv + 1) = 1 Hz */
#define RTC_SYNCH_PREDIV           ((uint32_t)0x1869)

time_t time_dat;
static int32_t cal_weekday(int32_t year, int32_t month, int32_t day);
RTC_HandleTypeDef hrtc;
int rtc_init(void)
{
  hrtc.Instance = RTC;
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  hrtc.Init.AsynchPrediv = RTC_ASYNCH_PREDIV;
  hrtc.Init.SynchPrediv = RTC_SYNCH_PREDIV;
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  HAL_RTC_Init(&hrtc);
  HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
  return 0;
}
INIT_PREV_EXPORT(rtc_init);

void rtc_get_time(rtc_time_t *rtc_time)
{
  RTC_DateTypeDef sdate;
  RTC_TimeTypeDef stime;

  HAL_RTC_GetTime(&hrtc, &stime, RTC_FORMAT_BCD);
  HAL_RTC_GetDate(&hrtc, &sdate, RTC_FORMAT_BCD);
  rtc_time->year = sdate.Year;
  rtc_time->month = sdate.Month;
  rtc_time->day = sdate.Date;
  rtc_time->weekday = sdate.WeekDay % 7;
  rtc_time->hour = stime.Hours;
  rtc_time->minute = stime.Minutes;
  rtc_time->second = stime.Seconds;
}

void rtc_set_time(rtc_time_t *rtc_time)
{
  RTC_DateTypeDef sdate;
  RTC_TimeTypeDef stime;

  struct tm orig;
  orig.tm_sec = RTC_Bcd2ToByte(rtc_time->second);
  orig.tm_min = RTC_Bcd2ToByte(rtc_time->minute);
  orig.tm_hour = RTC_Bcd2ToByte(rtc_time->hour);
  orig.tm_mday = RTC_Bcd2ToByte(rtc_time->day);
  orig.tm_mon = RTC_Bcd2ToByte(rtc_time->month)-1;
  orig.tm_year = RTC_Bcd2ToByte(rtc_time->year)+2000-1900;
  orig.tm_wday = cal_weekday(orig.tm_year+1900, orig.tm_mon+1, orig.tm_mday);
  orig.tm_isdst = -1;
  time_dat = mktime (&orig);

  sdate.Year =  rtc_time->year;
  sdate.Month = rtc_time->month;
  sdate.Date = rtc_time->day;
  sdate.WeekDay = rtc_time->weekday==0?7:rtc_time->weekday;
  stime.Hours = rtc_time->hour;
  stime.Minutes = rtc_time->minute;
  stime.Seconds = rtc_time->second;

  HAL_RTC_SetTime(&hrtc, &stime, RTC_FORMAT_BCD);
  HAL_RTC_SetDate(&hrtc, &sdate, RTC_FORMAT_BCD);
}

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
  time_dat += 1;
}

void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
  RCC_PeriphCLKInitTypeDef  PeriphClkInitStruct;
  rtc_time_t rtc_time;
  __HAL_RCC_PWR_CLK_ENABLE();
  HAL_PWR_EnableBkUpAccess();
  rtc_get_time(&rtc_time);
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV32;
  if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
    Error_Handler();
  }
  rtc_set_time(&rtc_time);
  __HAL_RCC_RTC_ENABLE();
  HAL_NVIC_SetPriority(RTC_IRQn, 3, 0);
  HAL_NVIC_EnableIRQ(RTC_IRQn);
}

void HAL_RTC_MspDeInit(RTC_HandleTypeDef *hrtc)
{
  __HAL_RCC_RTC_DISABLE();
}

void RTC_IRQHandler(void)
{
  rt_interrupt_enter();
  HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
  rt_interrupt_leave();
}

clock_t clock(void)
{
  return rt_tick_get();
}

time_t time (time_t *_timer)
{
  if(_timer != NULL) *_timer = time_dat;
  return time_dat;
}

static int32_t cal_weekday(int32_t year, int32_t month, int32_t day)
{
  int32_t weekday;
  if(month==1||month==2) {
    month+=12;
    year--;
	}
	weekday = (day+2*month+3*(month+1)/5+year+year/4-year/100+year/400)%7+1;
	return weekday==7?0:weekday;
}
